/*
 Copyright (C) 2011 QDSS.org. All rights reserved.
 QDSS.org PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package org.qdss.oapi4j.base.oauth;

import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.qdss.oapi4j.util.ApiUtils;
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.extractors.HeaderExtractor;
import org.scribe.extractors.HeaderExtractorImpl;
import org.scribe.model.OAuthConfig;
import org.scribe.model.OAuthConstants;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuth10aServiceImpl;
import org.scribe.oauth.OAuthService;
import org.scribe.services.TimestampService;
import org.scribe.services.TimestampServiceImpl;
import org.scribe.utils.URLUtils;

/**
 * QQ OAuth Api
 * 
 * @author <a href="mailto:zhaofang123@gmail.com">Zhao Fangfang</a>
 * @date 2011-3-25
 * @version $Id: QQApi.java 14 2011-04-01 06:54:32Z zhaofang123@gmail.com $
 */
public class QQApi extends DefaultApi10a {
	
	@Override
	public String getAuthorizationUrl(Token requestToken) {
		return null;
	}

	@Override
	public String getAccessTokenEndpoint() {
		return "https://open.t.qq.com/cgi-bin/access_token";
	}

	@Override
	public String getRequestTokenEndpoint() {
		return "https://open.t.qq.com/cgi-bin/request_token";
	}

	@Override
	public Verb getAccessTokenVerb() {
		return Verb.GET;
	}

	@Override
	public Verb getRequestTokenVerb() {
		return Verb.GET;
	}

	@Override
	public TimestampService getTimestampService() {
		return new TimestampServiceImpl2();
	}
	
	@Override
	public HeaderExtractor getHeaderExtractor() {
		return new HeaderExtractorImpl2();
	}

	@Override
	public OAuthService createService(OAuthConfig config, String scope) {
		return new OAuth10aServiceImpl2(this, config);
	}

	/**
	 */
	static class TimestampServiceImpl2 extends TimestampServiceImpl {
		/**
		 * 32位Nonce
		 */
		@Override
		public String getNonce() {
			String nonce = (super.getNonce()
					+ UUID.randomUUID().toString()).replaceAll("-", StringUtils.EMPTY);
			return nonce.substring(0, 32);
		}
	}
	
	/**
	 */
	static class HeaderExtractorImpl2 extends HeaderExtractorImpl {
		private static final String PARAM_SEPARATOR = ", ";
		private static final String PREAMBLE = "OAuth ";

		/**
		 * 参数排序
		 */
		@Override
		public String extract(OAuthRequest request) {
			Map<String, String> parameters = request.getOauthParameters();
			parameters = new TreeMap<String, String>(parameters);

			StringBuffer header = new StringBuffer(parameters.size() * 20);
			header.append(PREAMBLE);
			for (String key : parameters.keySet()) {
				if (header.length() > PREAMBLE.length()) {
					header.append(PARAM_SEPARATOR);
				}
				header.append(String.format("%s=\"%s\"", key, URLUtils
						.percentEncode(parameters.get(key))));
			}
			return header.toString();
		}
	}

	/**
	 * @see OAuth10aServiceImpl
	 */
	static class OAuth10aServiceImpl2 implements OAuthService {
		private static final String NO_SCOPE = null;
		private static final String VERSION = "1.0";
		private static final String PREAMBLE = "OAuth ";

		private OAuthConfig config;
		private DefaultApi10a api;
		private String scope;

		/**
		 * Default constructor
		 * 
		 * @param api OAuth1.0a api information
		 * @param config OAuth 1.0a configuration param object
		 */
		public OAuth10aServiceImpl2(DefaultApi10a api, OAuthConfig config) {
			this.api = api;
			this.config = config;
			this.scope = NO_SCOPE;
		}

		/**
		 * {@inheritDoc}
		 */
		public Token getRequestToken() {
			OAuthRequest request = new OAuthRequest(api.getRequestTokenVerb(), api.getRequestTokenEndpoint());
			addOAuthParams(request, OAuthConstants.EMPTY_TOKEN);
			addOAuthHeader(request);
			
			addQuerystringParams(request);
			Response response = request.send();
			return api.getRequestTokenExtractor().extract(response.getBody());
		}
		
		/**
		 * {@inheritDoc}
		 */
		public Token getAccessToken(Token requestToken, Verifier verifier) {
			OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
			request.addOAuthParameter(OAuthConstants.TOKEN, requestToken.getToken());
			request.addOAuthParameter(OAuthConstants.VERIFIER, verifier.getValue());
			addOAuthParams(request, requestToken);
			addOAuthHeader(request);
			
			addQuerystringParams(request);
			Response response = request.send();
			return api.getAccessTokenExtractor().extract(response.getBody());
		}
		
		/**
		 * {@inheritDoc}
		 */
		public void signRequest(Token token, OAuthRequest request) {
			request.addOAuthParameter(OAuthConstants.TOKEN, token.getToken());
			addOAuthParams(request, token);
			addOAuthHeader(request);
			addQuerystringParams(request);
		}

		private void addOAuthParams(OAuthRequest request, Token token) {
			request.addOAuthParameter(OAuthConstants.TIMESTAMP, api.getTimestampService().getTimestampInSeconds());
			request.addOAuthParameter(OAuthConstants.NONCE, api.getTimestampService().getNonce());
			request.addOAuthParameter(OAuthConstants.CONSUMER_KEY, config.getApiKey());
			request.addOAuthParameter(OAuthConstants.SIGN_METHOD, api.getSignatureService().getSignatureMethod());
			request.addOAuthParameter(OAuthConstants.VERSION, getVersion());
			// request_token时才需要加callback_url
			if (token == OAuthConstants.EMPTY_TOKEN)
				request.addOAuthParameter(OAuthConstants.CALLBACK, config.getCallback());
			if (scope != NO_SCOPE) 
				request.addOAuthParameter(OAuthConstants.SCOPE, scope);
			request.addOAuthParameter(OAuthConstants.SIGNATURE, getSignature(request, token));
		}
		
		/*-
		 * 将参数加在请求URL中
		 */
		private void addQuerystringParams(OAuthRequest request) {
			String querystr = request.getHeaders().get("Authorization");
			if (querystr != null) {
				String hs[] = querystr.split(",");
				if (hs[0].startsWith(PREAMBLE)) {
					hs[0] = hs[0].substring(PREAMBLE.length());
				}
				
				for (String h : hs) {
					String hv[] = h.trim().split("=");
					hv[1] = ApiUtils.decodeURL(hv[1].replaceAll("\"", StringUtils.EMPTY));
					request.addQuerystringParameter(hv[0], hv[1]);
				}
			}
		}

		/**
		 * {@inheritDoc}
		 */
		public String getVersion() {
			return VERSION;
		}

		/**
		 * {@inheritDoc}
		 */
		public void addScope(String scope) {
			this.scope = scope;
		}

		/**
		 * {@inheritDoc}
		 */
		public String getAuthorizationUrl(Token requestToken) {
			return api.getAuthorizationUrl(requestToken);
		}

		private String getSignature(OAuthRequest request, Token token) {
			String baseString = api.getBaseStringExtractor().extract(request);
			return api.getSignatureService().getSignature(baseString,
					config.getApiSecret(), token.getSecret());
		}

		private void addOAuthHeader(OAuthRequest request) {
			String oauthHeader = api.getHeaderExtractor().extract(request);
			request.addHeader(OAuthConstants.HEADER, oauthHeader);
		}
	}
}
